package com.xinqi.modules.user.users.user.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.ServiceException;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xinqi.common.core.constant.CacheConstants;
import com.xinqi.common.core.constant.ChatRemindConstants;
import com.xinqi.common.core.constant.ConfigKeyConstants;
import com.xinqi.common.core.constant.Constants;
import com.xinqi.common.core.enums.ShowInfoEnum;
import com.xinqi.common.core.enums.UeUserThirdBindTypeEnum;
import com.xinqi.common.core.enums.UserStatusEnum;
import com.xinqi.common.core.enums.UserTypeEnum;
import com.xinqi.common.core.utils.*;
import com.xinqi.common.core.utils.idCard.IdcardValidatorUtils;
import com.xinqi.common.redis.constants.UserCacheConstant;
import com.xinqi.common.redis.utils.Caches;
import com.xinqi.common.redis.utils.RedisUtils;
import com.xinqi.common.satoken.utils.LoginHelper;
import com.xinqi.modules.course.course.client.RemoteCourseClient;
import com.xinqi.modules.user.rongyuntemp.domain.RongYunTokenTempEntity;
import com.xinqi.modules.user.rongyuntemp.service.RongYunTokenTempService;
import com.xinqi.modules.user.third.dto.req.*;
import com.xinqi.modules.user.user.domain.UserEntity;
import com.xinqi.modules.user.user.domain.UserExtendEntity;
import com.xinqi.modules.user.user.dto.req.*;
import com.xinqi.modules.user.user.dto.req.v2.UpdatePhoneV2Dto;
import com.xinqi.modules.user.user.dto.rsp.*;
import com.xinqi.common.base.enums.QrCodeEnum;
import com.xinqi.common.base.enums.YesNoNumberEnum;
import com.xinqi.common.base.error.CommonErrors;
import com.xinqi.common.base.response.PageInfo;
import com.xinqi.common.base.response.Param;
import com.xinqi.common.base.response.R;
import com.xinqi.common.base.utils.change.ChangeTimes;
import com.xinqi.common.base.utils.json.JSON;
import com.xinqi.common.base.utils.spring.Spring;
import com.xinqi.common.mybatis.utils.Pages;
import com.xinqi.common.mybatis.utils.Queries;
import com.xinqi.common.third.oss.avatar.AvatarOssUtil;
import com.xinqi.common.third.util.TextUtil;
import com.xinqi.modules.chat.chat.friend.client.ChatFriendClient;
import com.xinqi.modules.chat.chat.group.client.RemoteChatClient;
import com.xinqi.modules.chat.chat.rongyun.RemoteRongyunClient;
import com.xinqi.modules.sys.config.client.SysConfigClient;
import com.xinqi.modules.user.users.third.domain.UserLoginLogEntity;
import com.xinqi.modules.user.users.third.domain.UserThirdBindEntity;
import com.xinqi.modules.user.users.third.manager.UserLoginLogManager;
import com.xinqi.modules.user.users.third.manager.UserThirdBindManager;
import com.xinqi.modules.user.users.user.convert.UserConvert;
import com.xinqi.modules.user.users.user.convert.UserV2Convert;
import com.xinqi.modules.user.users.user.manager.UserExtendManager;
import com.xinqi.modules.user.users.user.manager.UserManager;
import com.xinqi.modules.user.users.user.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * 用户信息服务层实现
 *
 * @author: wenjie
 * @date: 2023/05/17
 */
@Log4j2
@Service("userService")
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
    private final UserConvert userConvert;
    private final UserManager usersManager;

    private final UserLoginLogManager userLoginLogManager;

    private final UserExtendManager userExtendManager;

    private final UserThirdBindManager userThirdBindManager;

    private final RemoteRongyunClient remoteRongyunClient;

    private final RemoteCourseClient remoteCourseClient;

    private final RemoteChatClient remoteChatClient;

    private final ChatFriendClient remoteChatFriendClient;

    private final SysConfigClient sysConfigClient;

    private final RongYunTokenTempService rongYunTokenTempService;

    private final RedissonClient redissonClient;

    private final UserV2Convert userV2Convert;

    private final IdentifierGenerator identifierGenerator;

    /**
     * 分页查询 用户信息数据
     */
    @Override
    @DS("slaver")
    public PageInfo<UserResultDTO> page(Param pageable, UserQueryDTO query) {
        LambdaQueryWrapper<UserEntity> wrapper = Queries.lambda();
        // 排除新奇助手、提醒数据
        wrapper.ne(UserEntity::getId, ChatRemindConstants.REMIND);
        wrapper.ne(UserEntity::getId, ChatRemindConstants.ASSISTANT);
        //searchKey只支持手机号模糊搜索-后台管理系统专用
        Queries.accept(query.getSearchKey(), v -> wrapper.like(UserEntity::getMobile, v));

        Queries.accept(query.getId(), v -> wrapper.eq(UserEntity::getId, v));
        Queries.accept(query.getStatus(), v -> wrapper.eq(UserEntity::getStatus, v.getCode()));
        Queries.accept(query.getMobile(), v -> wrapper.eq(UserEntity::getMobile, v));
        Queries.accept(query.getStartTime(), v -> wrapper.ge(UserEntity::getCreateTime, v));
        Queries.accept(query.getEndTime(), v -> wrapper.le(UserEntity::getCreateTime, v));
        IPage<UserEntity> page = usersManager.page(Pages.page(pageable), wrapper);
        return Pages.convert(page, userConvert::convert);
    }

    @Override
    @DS("slaver")
    public PageInfo<UserResultDTO> pageAdmin(Param pageable, UserQueryDTO query) {
        LambdaQueryWrapper<UserEntity> wrapper = Queries.lambda();
        // 排除新奇助手、提醒数据
        wrapper.ne(UserEntity::getId, ChatRemindConstants.REMIND);
        wrapper.ne(UserEntity::getId, ChatRemindConstants.ASSISTANT);
        //searchKey只支持手机号模糊搜索-后台管理系统专用
        Queries.accept(query.getSearchKey(), v -> wrapper.like(UserEntity::getMobile, v));

        Queries.accept(query.getId(), v -> wrapper.eq(UserEntity::getId, v));
        Queries.accept(query.getStatus(), v -> wrapper.eq(UserEntity::getStatus, v.getCode()));
        Queries.accept(query.getMobile(), v -> wrapper.eq(UserEntity::getMobile, v));
        Queries.accept(query.getStartTime(), v -> wrapper.ge(UserEntity::getCreateTime, v));
        Queries.accept(query.getEndTime(), v -> wrapper.le(UserEntity::getCreateTime, v));
        IPage<UserEntity> page = usersManager.page(Pages.page(pageable), wrapper);
        page.getRecords().stream().forEach(item -> {
            item.setSalt(null);
            item.setPassword(null);
            item.setMobile(DesensitizedUtil.mobilePhone(item.getMobile()));
            item.setEmail(DesensitizedUtil.email(item.getEmail()));
            item.setNickname(DesensitizedUtil.chineseName(item.getNickname()));
        });
        return Pages.convert(page, userConvert::convert);
    }

    private LambdaQueryWrapper<UserEntity> wrapper(UsersQueryDTO query) {
        LambdaQueryWrapper<UserEntity> wrapper = Queries.lambda();
        Queries.accept(query.getId(), v -> wrapper.eq(UserEntity::getId, v));
        Queries.accept(query.getMobile(), v -> wrapper.like(UserEntity::getMobile, v));
        if (StringUtils.isNotEmpty(query.getSearchKey())) {
            wrapper.and(w1 -> w1.eq(UserEntity::getMobile, query.getSearchKey()).or(w3 -> w3.eq(UserEntity::getXinqiNumber, query.getSearchKey())));
        }
        return wrapper;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateStatus(UpdateUsersStatusDTO dto) {
        UsersUpdateDTO usersUpdateDTO = new UsersUpdateDTO();
        usersUpdateDTO.setId(dto.getUserId());
        usersUpdateDTO.setStatus(dto.getStatus().getCode());

        // 更新用户状态
        boolean b = this.updateById(usersUpdateDTO);
        // 如果用户被禁用，则让用户强制退出登录
        if (Objects.equals(dto.getStatus(), UserStatusEnum.DISABLE)) {
            Spring.committed(() -> LoginHelper.logout(dto.getUserId()));
        }
        return b;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean setAdmin(UsersSetAdminDTO dto) {
        UsersUpdateDTO userEntity = new UsersUpdateDTO();
        userEntity.setId(dto.getUserId());
        userEntity.setIsAdmin(dto.getIsAdmin().ordinal());
        return this.updateById(userEntity);
    }

    /**
     * 根据id修改 用户信息数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateById(UsersUpdateDTO dto) {
        Optional.ofNullable(usersManager.getById(dto.getId())).orElseThrow(() -> CommonErrors.BAD_REQUEST.asException("数据不存在"));
        UserEntity entity = userConvert.update(dto);
        return usersManager.updateById(entity);
    }

    @Override
    @DS("slaver")
    public UserResultDTO selectById(Long id) {
        UserResultDTO result = userConvert.convert(usersManager.findById(id));

        // 获取修改新奇号最大次数
        Integer maxTimes = Constants.UPDATE_XINQI_NUMBER_TIMES;
        try {
            String times = R.as(sysConfigClient.getConfigKey(ConfigKeyConstants.UPDATE_XINQI_NUMBER_TIMES));
            maxTimes = Integer.parseInt(times);
        } catch (Exception e) {
            log.info("获取系统配置新奇号失败");
        }
        result.setMaxTimes(maxTimes);

        if (Objects.isNull(result)) {
            return null;
        }
        List<UserThirdBindEntity> list = userThirdBindManager.findByUserId(id);
        list.stream().forEach(item -> {
            if (Objects.equals(UeUserThirdBindTypeEnum.QQ.getType(), item.getType() + StringUtils.EMPTY)) {
                result.setBindQQ(Boolean.TRUE);
            } else if (Objects.equals(UeUserThirdBindTypeEnum.WECHAT.getType(), item.getType() + StringUtils.EMPTY)) {
                result.setBindQQ(Boolean.TRUE);
            } else if (Objects.equals(UeUserThirdBindTypeEnum.RONGYUN.getType(), item.getType() + StringUtils.EMPTY)) {
                result.setRongyunToken(item.getCode());
            }
        });

        UserExtendEntity userExtendEntity = userExtendManager.findByUserId(id);
        if (Objects.nonNull(userExtendEntity)) {
            userConvert.convertExtend(result, userExtendEntity);
        }
        //二维码生成转化
        result.setQrCodeStr(result.getQrCode());
        result.setSalt(null);
        return result;
    }

    /**
     * 获取用户信息包含密码
     */
    @Override
    @DS("slaver")
    public UserPasswordResultDTO selectPasswordById(Long id) {
        UserPasswordResultDTO result = userConvert.convertPassword(usersManager.findById(id));
        if (Objects.isNull(result)) {
            return null;
        }
        List<UserThirdBindEntity> list = userThirdBindManager.findByUserId(id);
        list.stream().forEach(item -> {
            if (Objects.equals(UeUserThirdBindTypeEnum.QQ.getType(), item.getType() + StringUtils.EMPTY)) {
                result.setBindQQ(Boolean.TRUE);
            } else if (Objects.equals(UeUserThirdBindTypeEnum.WECHAT.getType(), item.getType() + StringUtils.EMPTY)) {
                result.setBindQQ(Boolean.TRUE);
            } else if (Objects.equals(UeUserThirdBindTypeEnum.RONGYUN.getType(), item.getType() + StringUtils.EMPTY)) {
                result.setRongyunToken(item.getCode());
            }
        });

        UserExtendEntity userExtendEntity = userExtendManager.findByUserId(id);
        if (Objects.nonNull(userExtendEntity)) {
            userConvert.convertExtend(result, userExtendEntity);
        }
        //二维码生成转化
        result.setQrCodeStr(result.getQrCode());
        return result;
    }

    @Override
    @DS("slaver")
    public UserResultDTO selectAdminById(Long id) {
        UserResultDTO result = userConvert.convert(usersManager.findById(id));
        if (Objects.isNull(result)) {
            return null;
        }
        List<UserThirdBindEntity> list = userThirdBindManager.findByUserId(id);
        list.stream().forEach(item -> {
            if (Objects.equals(UeUserThirdBindTypeEnum.QQ.getType(), item.getType() + StringUtils.EMPTY)) {
                result.setBindQQ(Boolean.TRUE);
            } else if (Objects.equals(UeUserThirdBindTypeEnum.WECHAT.getType(), item.getType() + StringUtils.EMPTY)) {
                result.setBindQQ(Boolean.TRUE);
            } else if (Objects.equals(UeUserThirdBindTypeEnum.RONGYUN.getType(), item.getType() + StringUtils.EMPTY)) {
                result.setRongyunToken(item.getCode());
            }
        });

        UserExtendEntity userExtendEntity = userExtendManager.findByUserId(id);
        if (Objects.nonNull(userExtendEntity)) {
            userConvert.convertExtend(result, userExtendEntity);
        }
        //二维码生成转化
        result.setQrCodeStr(result.getQrCode());
        result.setSalt(null);
        result.setMobile(DesensitizedUtil.mobilePhone(result.getMobile()));
        result.setEmail(DesensitizedUtil.email(result.getEmail()));
        result.setNickname(DesensitizedUtil.chineseName(result.getNickname()));
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateUserInfo(UpdateUserDto dto) {
        LambdaUpdateWrapper<UserEntity> lqw = new LambdaUpdateWrapper<>();
        if (StringUtils.isNotEmpty(dto.getAvatar())) {
            //修改头像 -- 头像不能为空
            lqw.set(UserEntity::getAvatar, dto.getAvatar());
            lqw.set(UserEntity::getAvatarFlag, dto.getAvatarFlag());
        }
        if (StringUtils.isNotEmpty(dto.getNickname())) {
            //修改昵称 -- 昵称不能为空
            lqw.set(UserEntity::getNickname, dto.getNickname());
        }
        if (StringUtils.isNotEmpty(dto.getSchoolName())) {
            //修改学校 -- 学校不能为空
            lqw.set(UserEntity::getSchoolId, dto.getSchoolId());
            lqw.set(UserEntity::getSchoolName, dto.getSchoolName());
        }
        //修改学号 -- 学号可以为空
        lqw.set(UserEntity::getStudentNumber, dto.getStudentNumber());
        //修改专业 -- 专业可以为空
        lqw.set(UserEntity::getMajor, dto.getMajor());

        lqw.eq(UserEntity::getId, dto.getUserId());
        usersManager.update(lqw);

        // 清理缓存
        Caches.del(redissonClient, UserCacheConstant.USER_KEY, dto.getUserId());
        UserEntity entity = usersManager.findById(dto.getUserId());
        //刷新融云信息
        remoteRongyunClient.updateUser(entity.getId().toString(), entity.getNickname(), entity.getAvatar());
        // 更新数据
        LoginHelper.updateXqLoginUser(userConvert.account(entity));

        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateXinqiNumber(String xinqiNumber, Long userId) {
        CommonErrors.BAD_REQUEST.check(xinqiNumber.length() < 13 && xinqiNumber.length() > 7, MessageUtils.message("sys.user.xinqiNumber.length.valid"));
        CommonErrors.BAD_REQUEST.check(PatternUtils.isAlphaNumeric(xinqiNumber), MessageUtils.message("sys.user.xinqiNumber.length.valid"));

        UserEntity userEntity = usersManager.findById(userId);
        CommonErrors.BAD_REQUEST.check(ObjectUtils.isNotEmpty(userEntity), MessageUtils.message("sys.user.info.not.blank"));
        if (StringUtils.isNotEmpty(userEntity.getXinqiNumber())) {
            CommonErrors.BAD_REQUEST.check(!userEntity.getXinqiNumber().equals(xinqiNumber), MessageUtils.message("sys.user.xinqiNumber.identical"));
        }
        CommonErrors.BAD_REQUEST.check(ObjectUtils.isEmpty(queryByXinqiNumber(xinqiNumber)), MessageUtils.message("sys.user.xinqiNumber.occupy"));

        UserExtendEntity extendEntity = userExtendManager.findByUserId(userEntity.getId());
        if (Objects.isNull(extendEntity)) {
            extendEntity = new UserExtendEntity();
            extendEntity.setUserId(userId);
        }

        // 获取修改新奇号最大次数
        Integer maxTimes = Constants.UPDATE_XINQI_NUMBER_TIMES;
        try {
            String times = R.as(sysConfigClient.getConfigKey(ConfigKeyConstants.UPDATE_XINQI_NUMBER_TIMES));
            maxTimes = Integer.parseInt(times);
        } catch (Exception e) {
            log.info("获取系统配置新奇号失败");
        }

        //修改新奇号后的新奇修改次数
        if (ObjectUtils.isNotEmpty(extendEntity.getXqUpdateTime()) && ObjectUtils.isNotEmpty(extendEntity.getTimes())) {
            int times = ChangeTimes.changeTimes(extendEntity.getXqUpdateTime().getYear(), LocalDateTime.now().getYear(), extendEntity.getTimes(), 1);
            CommonErrors.BAD_REQUEST.check(times < maxTimes, MessageUtils.message("sys.user.xinqiNumber.year.times"));
            extendEntity.setTimes(times + 1);
        } else {
            extendEntity.setTimes(1);
        }

        userEntity.setXinqiNumber(xinqiNumber);
        usersManager.updateById(userEntity);

        //设置二维码
        Map<String, Object> qrmap = new HashMap<String, Object>();
        qrmap.put("type", QrCodeEnum.QR_PERSONAL.getCode());
        qrmap.put("code", xinqiNumber);
        //获取二维码生成地址
        String genQrCodeUrl = R.as(sysConfigClient.getConfigKey(ConfigKeyConstants.SYS_GEN_QRCODE_URL));
        String content = genQrCodeUrl + JSON.stringify(qrmap);
        extendEntity.setQrCode(content);

        extendEntity.setXqUpdateTime(LocalDateTime.now());
        userExtendManager.saveOrUpdate(extendEntity);
        return true;
    }

    @Override
    public UserResultDTO queryByXinqiNumber(String xinqiNumber) {
        LambdaQueryWrapper<UserEntity> lqw = Wrappers.lambdaQuery();
        lqw.eq(UserEntity::getXinqiNumber, xinqiNumber);
        return Optional.ofNullable(usersManager.getOne(lqw)).map(userConvert::convert).orElse(null);

    }

    @Override
    public Boolean updatePrivacy(SetPrivacyDto dto) {
        UserExtendEntity extendEntity = userExtendManager.findByUserId(dto.getUserId());
        if (Objects.isNull(extendEntity)) {
            extendEntity = new UserExtendEntity();
            extendEntity.setUserId(dto.getUserId());
        }
        extendEntity.setSearchPhone(dto.getSearchPhone());

        // 公开个人信息
        if (ShowInfoEnum.checkIn(dto.getShowInfoConfig())) {
            extendEntity.setShowInfoConfig(dto.getShowInfoConfig());
        }

        // 通过手机号查询到自己
        if (Objects.equals(YesNoNumberEnum.YES.getCode(), dto.getShowMobileConfig())) {
            extendEntity.setShowMobileConfig(YesNoNumberEnum.YES.getCode());
        } else {
            extendEntity.setShowMobileConfig(YesNoNumberEnum.NO.getCode());
        }

        return userExtendManager.saveOrUpdate(extendEntity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updatePhone(UpdatePhoneDto dto) {
        UserEntity usersEntity = usersManager.findById(dto.getUserId());
        CommonErrors.BAD_REQUEST.check(ObjectUtils.isNotEmpty(usersEntity), MessageUtils.message("sys.user.info.not.blank"));
        if (StringUtils.isNotEmpty(usersEntity.getMobile())) {
            CommonErrors.BAD_REQUEST.check(!usersEntity.getMobile().equals(dto.getMobile()), MessageUtils.message("sys.user.mobile.identical"));
        }
        //更换手机号，验证码要新手机号的验证码
        CommonErrors.BAD_REQUEST.check(validateSmsCode(dto.getMobile(), dto.getSmsCode()), MessageUtils.message("sms.code.is.invalid"));

        //手机号唯一性校验
        CommonErrors.BAD_REQUEST.check(checkPhoneUnique(dto.getMobile(), dto.getUserId()), MessageUtils.message("sys.user.mobile.occupy"));

        usersEntity.setMobile(dto.getMobile());
        usersManager.updateById(usersEntity);

        UserExtendEntity extendEntity = userExtendManager.findByUserId(usersEntity.getId());
        if (Objects.isNull(extendEntity)) {
            extendEntity = new UserExtendEntity();
            extendEntity.setUserId(usersEntity.getId());
        }
        //修改手机号后的手机号修改次数
        if (ObjectUtils.isNotEmpty(extendEntity.getMobileUpdateTime()) && ObjectUtils.isNotEmpty(extendEntity.getMobileTimes())) {
            int times = ChangeTimes.changeTimes(extendEntity.getMobileUpdateTime().getYear(), LocalDateTime.now().getYear(), extendEntity.getMobileTimes(), 10);
            CommonErrors.BAD_REQUEST.check(times < 10, MessageUtils.message("sys.user.mobile.year.times"));
            extendEntity.setMobileTimes(times + 1);
        } else {
            extendEntity.setMobileTimes(1);
        }

        extendEntity.setMobileUpdateTime(LocalDateTime.now());
        boolean flag = userExtendManager.saveOrUpdate(extendEntity);
        if (flag) {
            // 所有端退出登录状态
            StpUtil.logout(usersEntity.getId());
        }
        return flag;
    }

    /**
     * 更换手机号
     *
     * @version 2.0
     * @author dzy
     */
    @Override
    public Boolean updatePhoneV2(UpdatePhoneV2Dto dto) {
        UserEntity user = usersManager.findById(dto.getUserId());
        CommonErrors.BAD_REQUEST.check(Md5HashUtil.checkPwd(user.getSalt(), dto.getPassword(), user.getPassword()), MessageUtils.message("sys.user.password.is.invalid"));
        return this.updatePhone(userV2Convert.convert(dto));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean bindEmail(UpdateEmailDto dto) {
        UserEntity usersEntity = usersManager.findById(dto.getUserId());
        CommonErrors.BAD_REQUEST.check(ObjectUtils.isNotEmpty(usersEntity), MessageUtils.message("sys.user.info.not.blank"));
        CommonErrors.BAD_REQUEST.check(StringUtils.isEmpty(usersEntity.getEmail()), MessageUtils.message("sys.user.email.is"));
        //邮箱验证码校验
        String code = RedisUtils.getCacheObject(CacheConstants.CAPTCHA_EMAIL_CODE_KEY + dto.getEmail());
        CommonErrors.BAD_REQUEST.check(dto.getSmsCode().equals(code), MessageUtils.message("ems.code.is.invalid"));
        //邮箱唯一性校验
        CommonErrors.BAD_REQUEST.check(checkEmailUnique(dto.getEmail(), dto.getUserId()), MessageUtils.message("sys.user.email.occupy"));

        usersEntity.setEmail(dto.getEmail());
        usersManager.updateById(usersEntity);

        UserExtendEntity extendEntity = userExtendManager.findByUserId(usersEntity.getId());
        if (Objects.isNull(extendEntity)) {
            extendEntity = new UserExtendEntity();
            extendEntity.setUserId(usersEntity.getId());
        }

        //修改邮箱后的邮箱修改次数
        if (ObjectUtils.isNotEmpty(extendEntity.getEmailUpdateTime()) && ObjectUtils.isNotEmpty(extendEntity.getEmailTimes())) {
            int times = ChangeTimes.changeTimes(extendEntity.getEmailUpdateTime().getYear(), LocalDateTime.now().getYear(), extendEntity.getEmailTimes(), 10);
            CommonErrors.BAD_REQUEST.check(times < 10, MessageUtils.message("sys.user.email.year.times"));
            extendEntity.setEmailTimes(times + 1);
        } else {
            extendEntity.setEmailTimes(1);
        }

        extendEntity.setEmailUpdateTime(LocalDateTime.now());
        return userExtendManager.saveOrUpdate(extendEntity);
    }

    @Override
    public Boolean checkPhoneUnique(String phonenumber, Long userId) {
        List<UserEntity> list = usersManager.list(new LambdaQueryWrapper<UserEntity>()
            .eq(UserEntity::getMobile, phonenumber)
            .eq(UserEntity::getIsDelete, YesNoNumberEnum.NO.getCode())
            .ne(UserEntity::getId, userId));
        return CollectionUtil.isEmpty(list);
    }

    @Override
    public Boolean checkEmailUnique(String email, Long userId) {
        List<UserEntity> list = usersManager.list(new LambdaQueryWrapper<UserEntity>().eq(UserEntity::getEmail, email).ne(UserEntity::getId, userId));
        return CollectionUtil.isEmpty(list);
    }

    @Override
    public Boolean unbindEmail(UpdateEmailDto dto) {
        //邮箱验证码校验
        String code = RedisUtils.getCacheObject(CacheConstants.CAPTCHA_EMAIL_CODE_KEY + dto.getEmail());
        CommonErrors.BAD_REQUEST.check(dto.getSmsCode().equals(code), MessageUtils.message("ems.code.is.invalid"));

        UserEntity user = usersManager.findById(dto.getUserId());
        CommonErrors.BAD_REQUEST.check(ObjectUtils.isNotEmpty(user), MessageUtils.message("sys.user.info.not.blank"));
        CommonErrors.BAD_REQUEST.check(StringUtils.isNotEmpty(user.getEmail()), MessageUtils.message("sys.user.email.not"));

        //tableField上设置了 updateStrategy = FieldStrategy.IGNORED 后就可以直接更新
        user.setEmail(null);
        return usersManager.updateById(user);
    }

    @Override
    public Boolean resetUserPwd(PasswordChangeDto dto) {
        UserEntity usersEntity = usersManager.findById(dto.getUserId());

        CommonErrors.BAD_REQUEST.check(ObjectUtils.isNotEmpty(usersEntity), MessageUtils.message("sys.user.info.not.blank"));
        //旧密码校验
        CommonErrors.BAD_REQUEST.check(Md5HashUtil.checkPwd(usersEntity.getSalt(), dto.getOldPassword(), usersEntity.getPassword()), MessageUtils.message("sys.user.password.old.is.invalid"));
        //新旧密码校验
        CommonErrors.BAD_REQUEST.check(!Md5HashUtil.checkPwd(usersEntity.getSalt(), dto.getNewPassword(), usersEntity.getPassword()), MessageUtils.message("sys.user.password.identical"));
        //新密码格式校验
        CommonErrors.BAD_REQUEST.check(dto.getNewPassword().length() < 19 && dto.getNewPassword().length() > 7, MessageUtils.message("sys.user.password.length.valid"));
        CommonErrors.BAD_REQUEST.check(PatternUtils.isAlphaNumeric(dto.getNewPassword()), MessageUtils.message("sys.user.password.valid"));
        //新密码与确认密码校验
        CommonErrors.BAD_REQUEST.check(dto.getNewPassword().equals(dto.getConfirmPassword()), MessageUtils.message("sys.user.password.confirm.identical"));

        usersEntity.setPassword(Md5HashUtil.encryptPwd(usersEntity.getSalt(), dto.getNewPassword()));

        return usersManager.updateById(usersEntity);
    }

    @Override
    public Boolean forgotPassword(ForgotChangeDto dto) {
        UserEntity usersEntity = usersManager.findById(dto.getUserId());
        CommonErrors.BAD_REQUEST.check(ObjectUtils.isNotEmpty(usersEntity), MessageUtils.message("sys.user.info.not.blank"));
        CommonErrors.BAD_REQUEST.check(ObjectUtils.isNotEmpty(dto.getConfirmPassword()), MessageUtils.message("sys.user.password.confirm.not.blank"));
        //手机号校验，留存手机号与接收验证码的手机号相同
        CommonErrors.BAD_REQUEST.check(Objects.equals(usersEntity.getMobile(), dto.getMobile()), MessageUtils.message("sys.user.password.mobile.not.identical"));
        //手机验证码校验
        CommonErrors.BAD_REQUEST.check(validateSmsCode(dto.getMobile(), dto.getSmsCode()), MessageUtils.message("sms.code.is.invalid"));
        //新密码格式校验
        CommonErrors.BAD_REQUEST.check(dto.getNewPassword().length() < 19 && dto.getNewPassword().length() > 7, MessageUtils.message("sys.user.password.length.valid"));
        CommonErrors.BAD_REQUEST.check(PatternUtils.isAlphaNumeric(dto.getNewPassword()), MessageUtils.message("sys.user.password.valid"));
        //新旧密码校验
        CommonErrors.BAD_REQUEST.check(!Md5HashUtil.checkPwd(usersEntity.getSalt(), dto.getNewPassword(), usersEntity.getPassword()), MessageUtils.message("sys.user.password.identical"));
        //新密码与确认密码校验
        CommonErrors.BAD_REQUEST.check(dto.getNewPassword().equals(dto.getConfirmPassword()), MessageUtils.message("sys.user.password.confirm.identical"));

        usersEntity.setSalt(Md5HashUtil.randomSalt());
        usersEntity.setPassword(Md5HashUtil.encryptPwd(usersEntity.getSalt(), dto.getNewPassword()));
        return usersManager.updateById(usersEntity);
    }

    @Override
    public Boolean authentication(UserAuthenticationDto dto) throws Exception {
        CommonErrors.BAD_REQUEST.check(ObjectUtils.isNotEmpty(dto.getIdCard()), MessageUtils.message("sys.user.idcard.not.blank"));
        CommonErrors.BAD_REQUEST.check(ObjectUtils.isNotEmpty(dto.getRealName()), MessageUtils.message("sys.user.realname.not.blank"));
        //身份证格式校验
        CommonErrors.BAD_REQUEST.check(IdcardValidatorUtils.isValidatedAllIdcard(dto.getIdCard()), MessageUtils.message("sys.user.idcard.not.valid"));
        //身份证唯一
        CommonErrors.BAD_REQUEST.check(Objects.isNull(userExtendManager.selectByIdCard(dto.getIdCard())), MessageUtils.message("sys.user.idcard.occupy"));


        UserEntity usersEntity = usersManager.findById(dto.getUserId());
        CommonErrors.BAD_REQUEST.check(ObjectUtils.isNotEmpty(usersEntity), MessageUtils.message("sys.user.info.not.blank"));

        UserExtendEntity extendEntity = userExtendManager.findByUserId(dto.getUserId());
        if (Objects.isNull(extendEntity)) {
            extendEntity = new UserExtendEntity();
            extendEntity.setUserId(dto.getUserId());
        }
        if (ObjectUtils.isNotEmpty(extendEntity.getRealName()) && ObjectUtils.isNotEmpty(extendEntity.getIdCard())) {
            CommonErrors.BAD_REQUEST.check(false, MessageUtils.message("sys.user.authentication.has.undergone"));
        }

        String appCode = "06fcbe779ccd47e38afd21db1dcbe826";
        String host = "http://smrz.qianshutong.com";
        String path = "/web/interface/grsfyz";
        String method = "POST";

        Map<String, String> headers = new HashMap<>();
        //最后在header中的格式(中间是英文空格)为Authorization:APPCODE 83359fd73fe94948385f570e3c139105
        headers.put("Authorization", "APPCODE " + appCode);
        //根据API的要求，定义相对应的Content-Type
        headers.put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
        Map<String, String> querys = new HashMap<>();
        Map<String, String> bodys = new HashMap<>();
        bodys.put("idcard", dto.getIdCard());
        bodys.put("name", dto.getRealName());
        /*
         * 重要提示如下:
         * HttpUtils请从
         * https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/src/main/java/com/aliyun/api/gateway/demo/util/HttpUtils.java
         * 相应的依赖请参照
         * https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/pom.xml
         */
        HttpResponse response = AliyunHttpUtils.doPost(host, path, method, headers, querys, bodys);
        String result = EntityUtils.toString(response.getEntity());
        JSONObject resultJson = JSONObject.parseObject(result);
        try {
            if (resultJson.get("code").equals(200)) { //请求调用成功
                String jsonString = resultJson.getString("data");
                JSONObject data1 = JSONObject.parseObject(jsonString);
                JSONObject data2 = data1.getJSONObject("data");
                if (Objects.equals(1, data2.get("result"))) {
                    //认证成功
                    extendEntity.setRealName(dto.getRealName());
                    extendEntity.setIdCard(dto.getIdCard());
                    userExtendManager.saveOrUpdate(extendEntity);
                } else {
                    //认证失败
                    CommonErrors.BAD_REQUEST.check(false, MessageUtils.message("sys.user.authentication.fail"));
                }
            } else {
                CommonErrors.BAD_REQUEST.check(false, MessageUtils.message("sys.user.authentication.fail"));
            }
        } catch (Exception e) {
            CommonErrors.BAD_REQUEST.check(false, MessageUtils.message("sys.user.authentication.fail"));
        }

        return true;
    }

    /**
     * 账号注销
     *
     * @param userId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void accountCancel(Long userId) {
        /*
         * 课程注销与聊天注销异步执行
         * 注销成功后再执行用户注销
         * 防止用户数据不存在导致其他注销失败
         * <p>
         * JDK 8 CompletableFuture
         */
        CompletableFuture<Void> task1 = CompletableFuture.runAsync(() -> {
            //课程注销
            remoteCourseClient.accountCancel(userId);
        });

        CompletableFuture<Void> task2 = CompletableFuture.runAsync(() -> {
            //聊天注销
            remoteChatClient.accountCancel(userId);
        });

        CompletableFuture.allOf(task1, task2).thenRun(() -> {
            //执行完成后再执行
            log.info("用户 => {},注销成功", userId);
            //用户注销
            deleteUserInfo(userId);
            //退出登录
            Spring.committed(() -> LoginHelper.logout(userId));
        });

    }

    @Override
    public UserResultDTO registerXqAppUser(UserRegisterDTO dto) {

        // 获取融云临时用户信息
        RongYunTokenTempEntity tempEntity = rongYunTokenTempService.getRongYunToken();
        CommonErrors.BAD_REQUEST.check(Objects.nonNull(tempEntity), "注册失败");

        // 生成用户信息
        UserEntity usersEntity = new UserEntity();
        usersEntity.setId(tempEntity.getId());
        usersEntity.setMobile(dto.getMobile());
        usersEntity.setSchoolId(0L);
        usersEntity.setSchoolName(dto.getSchoolName());
        usersEntity.setMajor(dto.getMajor());
        // 判断是否已设置密码
        if (StringUtils.isNotEmpty(dto.getPassword())) {
            usersEntity.setSalt(Md5HashUtil.randomSalt());
            usersEntity.setPassword(Md5HashUtil.encryptPwd(usersEntity.getSalt(), dto.getPassword()));
        } else {
            String initPassword = R.as(sysConfigClient.getConfigKey(ConfigKeyConstants.SYS_USER_INIT_PASSWORD));
            usersEntity.setSalt(Md5HashUtil.randomSalt());
            usersEntity.setPassword(Md5HashUtil.encryptPwd(usersEntity.getSalt(), initPassword));
        }
        // 昵称生成
        usersEntity.setNickname(StringUtils.isEmpty(dto.getNickName()) ? DesensitizedUtil.mobilePhone(dto.getSchoolName()) : dto.getNickName());

        // 生成文字图片
        String avatar = dto.getAvatar();
        if (StringUtils.isEmpty(avatar)) {
            avatar = avatarOSSUrl(dto.getNickName());
        }
        usersEntity.setAvatar(avatar);

        usersEntity.setStatus(UserStatusEnum.ENABLE.getCode());
        usersEntity.setXinqiNumber(this.checkXqNum());
        usersManager.save(usersEntity);

        // 生成额为字段信息
        UserExtendEntity userExtendEntity = new UserExtendEntity();
        userExtendEntity.setId(usersEntity.getId());
        userExtendEntity.setUserId(usersEntity.getId());
        userExtendEntity.setShowMobileConfig(0);
        userExtendEntity.setShowInfoConfig(0);
        userExtendEntity.setSearchPhone(1);
        userExtendEntity.setUserRole(dto.getUserRole());
        // 生成二维码信息
        Map<String, Object> qrmap = new HashMap<>();
        qrmap.put("type", QrCodeEnum.QR_PERSONAL.getCode());
        qrmap.put("code", usersEntity.getXinqiNumber());
        String genQrCodeUrl = R.as(sysConfigClient.getConfigKey(ConfigKeyConstants.SYS_GEN_QRCODE_URL));
        String content = genQrCodeUrl + JSON.stringify(qrmap);
        userExtendEntity.setQrCode(content);
        this.direct(userExtendEntity);

        // 返回信息转换
        UserResultDTO userResultDTO = userConvert.convert(usersEntity);
        userResultDTO.setNotExist(true);
        // 返回注册用户角色
        userResultDTO.setUserRole(userExtendEntity.getUserRole());

        //注册时初始化好友列表
        remoteChatFriendClient.initFriend(userResultDTO.getId());

        // 生成融云TOKEN记录
        createRongYunToken(tempEntity);
        userResultDTO.setRongyunToken(tempEntity.getRongyunToken());

        // 异步更新融云用户信息
        Spring.committedAsync(() -> {
            remoteRongyunClient.updateUser(usersEntity.getId().toString(), usersEntity.getNickname(), usersEntity.getAvatar());
        });

        return userResultDTO;
    }

    /**
     * 获取头像上传阿里云
     */
    private static String avatarOSSUrl(String str) {
        String name = TextUtil.getLastNumStr(str, 2);
        String ossUrl = AvatarOssUtil.creatAvatar(name);
        return ossUrl;
    }

    @Override
    @DS("slaver")
    public List<UserResultDTO> list(UserQueryDTO query) {
        LambdaQueryWrapper<UserEntity> wrapper = Queries.lambda();
        wrapper.ne(UserEntity::getId, ChatRemindConstants.REMIND);
        wrapper.ne(UserEntity::getId, ChatRemindConstants.ASSISTANT);
        //用户列表，只查真实用户
        wrapper.eq(UserEntity::getType, UserTypeEnum.REAL.getCode());
        Queries.accept(query.getId(), v -> wrapper.eq(UserEntity::getId, v));
        if (StringUtils.isNotEmpty(query.getSearchKey())) {
            wrapper.and(w1 -> w1.eq(UserEntity::getMobile, query.getSearchKey()).or(w3 -> w3.eq(UserEntity::getXinqiNumber, query.getSearchKey())));
        }
        return usersManager.list(wrapper).stream().map(userConvert::convert).collect(Collectors.toList());
    }

    /**
     * 关键字搜索 -- 新奇号、手机号精准查询
     *
     * @param dto
     * @return
     */
    @Override
    @DS("slaver")
    public List<SearchFriendResultDTO> selectUsersByKeyword(SearchFriendDTO dto) {
        List<SearchFriendResultDTO> list = usersManager.selectUsersByKeyword(dto);
        //Iterator 迭代器 移除新奇提醒、新奇助手
        Iterator<SearchFriendResultDTO> it = list.iterator();
        while (it.hasNext()) {
            SearchFriendResultDTO item = it.next();
            if (Objects.equals(ChatRemindConstants.REMIND, item.getId().toString())) {
                it.remove();
            } else if (Objects.equals(ChatRemindConstants.ASSISTANT, item.getId().toString())) {
                it.remove();
            }
        }
        return list;
    }

    @Override
    @DS("slaver")
    public List<UserResultDTO> findList(List<Long> userIds) {
        LambdaQueryWrapper<UserEntity> wrapper = Queries.lambda();
        wrapper.in(UserEntity::getId, userIds);
        wrapper.ne(UserEntity::getId, ChatRemindConstants.REMIND);
        wrapper.notLike(UserEntity::getId, ChatRemindConstants.ASSISTANT);
        return usersManager.list(wrapper).stream().map(userConvert::convert).collect(Collectors.toList());
    }

    @Override
    @DS("slaver")
    public UserResultDTO findByMobile(String mobile) {
        LambdaQueryWrapper<UserEntity> wrapper = Queries.lambda();
        wrapper.eq(UserEntity::getMobile, mobile);
        wrapper.eq(UserEntity::getType, UserTypeEnum.REAL.getCode());

        return userConvert.convert(usersManager.getOne(wrapper));
    }

    @Override
    @DS("slaver")
    public List<UserResultDTO> findByMobile(List<String> mobileList) {
        LambdaQueryWrapper<UserEntity> wrapper = Queries.lambda();
        wrapper.in(UserEntity::getMobile, mobileList)
            .eq(UserEntity::getIsDelete, YesNoNumberEnum.NO.getCode());
        List<UserResultDTO> list = usersManager.list(wrapper).stream().map(userConvert::convert).collect(Collectors.toList());
        //Iterator 迭代器 移除新奇提醒、新奇助手
        Iterator<UserResultDTO> it = list.iterator();
        while (it.hasNext()) {
            UserResultDTO item = it.next();
            if (Objects.equals(ChatRemindConstants.REMIND, item.getId().toString())) {
                it.remove();
            } else if (Objects.equals(ChatRemindConstants.ASSISTANT, item.getId().toString())) {
                it.remove();
            }
        }
        return list;
    }

    @Override
    public UserResultDTO getXqUserInfoByPhone(String mobile) {
        LambdaQueryWrapper<UserEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(UserEntity::getMobile, mobile);
        lambdaQueryWrapper.eq(UserEntity::getType, UserTypeEnum.REAL.getCode());
        UserEntity user = usersManager.getOne(lambdaQueryWrapper);
        if (user == null) {
            return null;
        }

        UserResultDTO userResultDTO = userConvert.convert(user);

        UserExtendEntity extendEntity = userExtendManager.findByUserId(user.getId());
        if (Objects.nonNull(extendEntity)) {
            userResultDTO.setUserRole(extendEntity.getUserRole());
        }

        UserThirdBindEntity rongyun = userThirdBindManager.findByTypeAndUserId(UeUserThirdBindTypeEnum.RONGYUN.getType(), user.getId());
        if (Objects.isNull(rongyun)) {
            R<String> result = this.remoteRongyunClient.registUser(user.getId().toString(), user.getNickname(), user.getAvatar());
            if (result.getCode() == 200) {
                UserThirdCheckDTO checkDto = new UserThirdCheckDTO();
                checkDto.setIdentification(result.getData());
                checkDto.setType("1");
                checkDto.setUserId(user.getId());
                thirdBindUser(checkDto);
                userResultDTO.setRongyunToken(result.getData());
            }
        } else {
            userResultDTO.setRongyunToken(rongyun.getCode());
        }
        return userResultDTO;
    }

    private Boolean deleteUserInfo(Long userId) {
        //设备token第三方绑定信息删除
        userLoginLogManager.deleteByUserId(userId);
        userThirdBindManager.deleteByUserId(userId);

        //删除用户主表信息
        usersManager.deleteById(userId);
        userExtendManager.deleteByUserId(userId);
        return true;
    }

    private String checkXqNum() {
        boolean flag = true;
        int maxNum = 0;
        String xinqiNumber = "";
        while (flag) {
            maxNum++;
            Long time = System.currentTimeMillis();
            Long random = RandomUtil.randomLong(0, 99999999);
            long endTime = time + random;
            xinqiNumber = "XQ" + Long.toString(endTime).substring(5, 13);
            UserEntity user = usersManager.getOne(new LambdaQueryWrapper<UserEntity>().eq(UserEntity::getXinqiNumber, xinqiNumber).last("limit 1"));
            if (user == null) {
                flag = false;
            }
            if (maxNum > 100) {
                flag = false;
            }
        }
        return xinqiNumber;
    }

    private boolean direct(UserExtendEntity userExtendEntity) {
        String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
        Long time = DateUtils.getNowDateTime();
        Integer loginFailure = 0;
        userExtendEntity.setLoginIp(ip);
        userExtendEntity.setLoginTime(time);
        userExtendEntity.setLoginFailure(loginFailure);
        userExtendManager.saveOrUpdate(userExtendEntity);
        return true;
    }

    /**
     * 生成融云TOKEN
     */
    private void createRongYunToken(RongYunTokenTempEntity entity) {
        UserThirdBindEntity userThirdBindEntity = new UserThirdBindEntity();
        userThirdBindEntity.setId(entity.getId());
        userThirdBindEntity.setUserId(entity.getId());
        userThirdBindEntity.setCode(entity.getRongyunToken());
        userThirdBindEntity.setType(UeUserThirdBindTypeEnum.RONGYUN.getType());
        userThirdBindManager.save(userThirdBindEntity);
    }

    @Override
    public UserResultDTO thirdBindUser(UserThirdCheckDTO thirdCheck) {
        String type = thirdCheck.getType();
        UeUserThirdBindTypeEnum ueUserThirdBindTypeEnum = UeUserThirdBindTypeEnum.getType(type);
        if (ObjectUtil.isNull(ueUserThirdBindTypeEnum)) {
            throw new ServiceException("用户类型错误，绑定类型 1:融云token 2:微信 3:qq 4:appleid");
        }
        userThirdBindManager.deleteByUserId(thirdCheck.getUserId());
        UserThirdBindEntity thirdBind = new UserThirdBindEntity();
        thirdBind.setUserId(thirdCheck.getUserId());
        thirdBind.setType(ueUserThirdBindTypeEnum.getType());
        thirdBind.setCode(thirdCheck.getIdentification());
        userThirdBindManager.save(thirdBind);
        return userThirdBindManager.thirdLoginCheck(ueUserThirdBindTypeEnum.getType(), thirdCheck.getIdentification());
    }

    @Override
    public UserResultDTO registerLastStep(RegisterLastStepDTO lastStep) {
        return userConvert.convert(usersManager.findById(LoginHelper.getUserId()));
    }

    @Override
    public Boolean resetPassword(PasswordResetDTO dto) {
        UserEntity user = new UserEntity();
        user.setId(dto.getId());
        user.setSalt(dto.getSalt());
        user.setPassword(dto.getPassword());
        return usersManager.updateById(user);
    }

    @Override
    public void generateLog(UserLoginLogEntity log) {
        userLoginLogManager.generateLog(log);
    }

    /**
     * 校验手机号码是否为新用户
     *
     * @param mobile
     * @return
     */
    @Override
    public Boolean findByMobileIncludeDelete(String mobile) {
        List<UserEntity> list = usersManager.findByMobileIncludeDelete(mobile);
        return CollectionUtil.isEmpty(list);
    }

    /**
     * 校验手机号码获取新用户信息-包含注销用户
     * @param mobile
     * @return
     */
    @Override
    public List<UserBaseInfoDO> findListByMobileIncludeDelete(String mobile) {
        return userConvert.userBaseInfoList(usersManager.findByMobileIncludeDelete(mobile));
    }

    @Override
    public UserBaseInfoDO findUserBaseInfo(Long userId) {
        return userConvert.userBaseInfo(usersManager.findById(userId));
    }

    /**
     * 验证码校验
     *
     * @param mobile
     * @param smsCode
     * @return
     */
    private boolean validateSmsCode(String mobile, String smsCode) {
        // TODO dzy 上线去除万能验证码 666666
        if (Objects.equals("666666", smsCode)) {
            return true;
        }
        CommonErrors.BAD_REQUEST.check(StringUtils.isNotEmpty(mobile), MessageUtils.message("user.phonenumber.not.blank"));
        CommonErrors.BAD_REQUEST.check(StringUtils.isNotEmpty(smsCode), MessageUtils.message("user.jcaptcha.not.blank"));
        String code = RedisUtils.getCacheObject(CacheConstants.CAPTCHA_CODE_KEY + mobile);
        CommonErrors.BAD_REQUEST.check(StringUtils.isNotEmpty(code), MessageUtils.message("user.jcaptcha.is_blank"));
        boolean checkFlag = Objects.equals(code, smsCode);
        if (checkFlag) {
            RedisUtils.deleteObject(CacheConstants.CAPTCHA_CODE_KEY + mobile);
        }
        return checkFlag;
    }

    @Override
    public UserLoginResultDTO selectLoginMsgByMobile(String mobile) {
        UserLoginResultDTO user = usersManager.selectUserLoginByMobile(mobile);
        if (Objects.nonNull(user) && StringUtils.isEmpty(user.getRongyunToken())) {
            R<String> result = this.remoteRongyunClient.registUser(user.getId().toString(), user.getNickname(), user.getAvatar());
            if (result.getCode() == 200 && Objects.nonNull(result.getData())) {
                UserThirdCheckDTO checkDto = new UserThirdCheckDTO();
                checkDto.setIdentification(result.getData());
                checkDto.setType(UeUserThirdBindTypeEnum.RONGYUN.getType());
                checkDto.setUserId(user.getId());
                thirdBindUser(checkDto);
                user.setRongyunToken(result.getData());
            }
        }
        return user;
    }

    @Override
    public UserLoginResultDTO selectLoginMsgById(Long id) {
        UserLoginResultDTO user = usersManager.selectUserLoginById(id);
        if (user == null) {
            return null;
        }
        if (StringUtils.isEmpty(user.getRongyunToken())) {
            R<String> result = this.remoteRongyunClient.registUser(user.getId().toString(), user.getNickname(), user.getAvatar());
            if (result.getCode() == 200 && Objects.nonNull(result.getData())) {
                UserThirdCheckDTO checkDto = new UserThirdCheckDTO();
                checkDto.setIdentification(result.getData());
                checkDto.setType(UeUserThirdBindTypeEnum.RONGYUN.getType());
                checkDto.setUserId(user.getId());
                thirdBindUser(checkDto);
                user.setRongyunToken(result.getData());
            }
        }
        return user;
    }

    /**
     * 账号封禁（用户投诉处理）
     *
     * @param userIds
     * @return
     */
    @Override
    public Boolean accountBan(List<Long> userIds) {
        if (CollectionUtil.isEmpty(userIds)) {
            return Boolean.TRUE;
        }
        LambdaUpdateWrapper<UserEntity> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(UserEntity::getStatus, UserStatusEnum.DISABLE.getCode())
            .in(UserEntity::getId, userIds);
        boolean flag = usersManager.update(updateWrapper);
        Spring.committed(() -> {
            if (flag) {
                //清理缓存，此处需需要前端做下线处理，收到封禁通知，显示并下线
                userIds.stream().forEach(userId -> {
                    Caches.del(redissonClient, UserCacheConstant.USER_KEY, userId);
                });
            }
        });

        return flag;
    }

    /**
     * 账号解封（用户投诉处理）
     *
     * @param userIds
     * @return
     */
    @Override
    public Boolean accountUnblocking(List<Long> userIds) {
        LambdaUpdateWrapper<UserEntity> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(UserEntity::getStatus, UserStatusEnum.ENABLE.getCode())
            .in(UserEntity::getId, userIds);
        boolean flag = usersManager.update(updateWrapper);
        Spring.committed(() -> {
            if (flag) {
                //清理缓存
                userIds.stream().forEach(userId -> {
                    Caches.del(redissonClient, UserCacheConstant.USER_KEY, userId);
                });
            }
        });

        return flag;
    }

    /**
     * 注册助手用户
     *
     * @return
     */
    @Override
    public UserResultDTO registerAssistant() {
        LambdaQueryWrapper<UserEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(UserEntity::getId, ChatRemindConstants.ASSISTANT);
        wrapper.orderByDesc(UserEntity::getId).last("limit 1");
        UserEntity entity = usersManager.getOne(wrapper);
        Long mobile = Long.parseLong(entity.getMobile()) + 1L;
        entity.setMobile(mobile.toString());
        entity.setXinqiNumber("XQZS" + mobile);
        if (Objects.equals(ChatRemindConstants.ASSISTANT, entity.getId().toString())) {
            entity.setId(entity.getId() * 1000000);
        } else {
            entity.setId(entity.getId() + 1);
        }
        usersManager.save(entity);
        return userConvert.convert(entity);
    }

    /**
     * 注册模拟用户
     *
     * @param dto
     * @return
     */
    @Override
    public UserResultDTO imitateRegister(UserImitateDTO dto) {

        // 查询模拟用户是否存在
        LambdaQueryWrapper<UserEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserEntity::getNickname, dto.getNickName());
        wrapper.eq(UserEntity::getMobile, dto.getMobile());
        wrapper.eq(UserEntity::getType, UserTypeEnum.IMITATE.getCode());
        UserEntity entity = usersManager.getOne(wrapper);

        // 模拟用户不存在-创建模拟用户
        if (Objects.isNull(entity)) {
            entity = new UserEntity();
            entity.setNickname(dto.getNickName());
            entity.setMobile(dto.getMobile());
            entity.setXinqiNumber(this.checkXqNum());
            entity.setAvatar(avatarOSSUrl(dto.getNickName()));
            entity.setType(UserTypeEnum.IMITATE.getCode());
            entity.setIdentify(identifierGenerator.nextId(null).longValue());
            usersManager.save(entity);
        }

        return userConvert.convert(entity);

    }

    @Override
    @DS("slaver")
    public UserResultDTO findImitateByMobileAndNickname(String mobile, String nickname) {
        List<UserEntity> list = usersManager.list(wrapper(mobile, nickname, UserTypeEnum.IMITATE.getCode()));
        if (CollectionUtil.isNotEmpty(list)) {
            return userConvert.convert(list.get(0));
        } else {
            return null;
        }
    }

    @Override
    public void imitateUserUpdate(UserImitateUpdateDTO dto) {
        LambdaUpdateWrapper<UserEntity> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        if (Objects.nonNull(dto.getUserId())) {
            lambdaUpdateWrapper.eq(UserEntity::getId, dto.getUserId());
        } else {
            lambdaUpdateWrapper.eq(UserEntity::getMobile, dto.getOldMobile());
            lambdaUpdateWrapper.eq(UserEntity::getNickname, dto.getOldNickName());
            lambdaUpdateWrapper.eq(UserEntity::getType, UserTypeEnum.IMITATE.getCode());
        }

        if (StringUtils.isNotEmpty(dto.getNickName())) {
            lambdaUpdateWrapper.set(UserEntity::getNickname, dto.getNickName());
            try {
                String ossAvatar = AvatarOssUtil.creatAvatar(TextUtil.getLastNumStr(dto.getNickName(), 2));
                lambdaUpdateWrapper.set(UserEntity::getAvatar, ossAvatar);
            } catch (Exception e) {
                log.info("生成用户头像失败：{}", e.toString());
            }
        }

        if (StringUtils.isNotEmpty(dto.getMobile())) {
            lambdaUpdateWrapper.set(UserEntity::getMobile, dto.getMobile());
        }

        // 学号必传 老师 学生修改 学号必传字段
        lambdaUpdateWrapper.set(UserEntity::getStudentNumber, dto.getStudentNumber());

        this.usersManager.update(lambdaUpdateWrapper);
    }

    private LambdaQueryWrapper<UserEntity> wrapper(String mobile, String nickname, Integer type) {
        LambdaQueryWrapper<UserEntity> wrapper = Queries.lambda();
        Queries.accept(mobile, v -> wrapper.eq(UserEntity::getMobile, v));
        Queries.accept(nickname, v -> wrapper.eq(UserEntity::getNickname, v));
        Queries.accept(type, v -> wrapper.eq(UserEntity::getType, v));
        return wrapper;
    }

}
